package com.ahwei.servlet;

import com.ahwei.util.*;
import com.ahwei.util.anno.Controller;
import com.ahwei.util.anno.RequestMapping;
import lombok.Data;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author：ahwei163@qq.com
 *
 * @date: 202*-03-15 11:29:44
 * @description: 这是一个中央调度器
 */
@WebServlet(name = "DispatcherServlet", value = "/", initParams = {
        @WebInitParam(name = "prefix", value = "html/"),
        @WebInitParam(name = "suffix", value = ".html")})
public class DispatcherServlet extends HttpServlet {
    //Spring中的工具类，可以把方法中的形参名解析出来
    static LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer =
            new LocalVariableTableParameterNameDiscoverer();

    //上下文路径
    private static String contextPath;
    private String prefix, suffix;
    private Map<String, URLInfo> urlInfoMap;

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);

        urlInfoMap = new HashMap<>();
        //从web.xml文件读取前缀和后缀配置
        prefix = getInitParameter("prefix");
        suffix = getInitParameter("suffix");
        //获取上下文路径
        contextPath = this.getServletContext().getContextPath();
        //获取controller包下得所有有Controller注解的类
        Set<Class> ControllerSet = ScanUtil.scanPackage4AnnotatedClass("com.ahwei.controller", Controller.class);


        //遍历类集合,创建url映射目录
        for (Class clazz : ControllerSet) {
            //实例化类
            Object obj = null;
            try {
                obj = clazz.getDeclaredConstructor().newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
            //遍历类中的方法集合，创建url与方法的映射关系
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                //获取方法上的RequestMapping注解
                RequestMapping rm = method.getAnnotation(RequestMapping.class);
                //如果RequestMapping注解存在，创建url映射目录
                if (rm != null) {
                    //获取注解中去除扩展名的url
                    String url = rm.value();
                    int lastIndex = url.lastIndexOf(".");
                    lastIndex = lastIndex < 0 ? url.length() : lastIndex;

                    //获取url并与上下文路径组成的完整url
                    url = contextPath + url.substring(0, lastIndex);

                    //建立url与controller类的关系
                    urlInfoMap.put(url, new URLInfo(url, obj, method));
                }
            }
        }
        //打印输出全部的url请求
        urlInfoMap.keySet().forEach(System.out::println);

    }


    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        //设置编码
        request.setCharacterEncoding("UTF-8");
        //response.setContentType("text/html;charset=utf-8");
        //获取用户请求的uri
        String requestURI = request.getRequestURI();
        //发射调用uri映射的方法
        Object result = null;
        try {
            //System.out.println("urlInfoMap.get(requestURI) = " + urlInfoMap.get(requestURI));
            if (urlInfoMap.get(requestURI) != null) {
                result = urlInfoMap.get(requestURI).invoke(request, response);
                Thread.sleep(500);
            } else {
                result = StringUtils.buildExceptionMsg(requestURI + "接口不存在");
            }
        } catch (Exception e) {
            e.printStackTrace();
            LogUtils.info(requestURI + "响应参数: 发生异常 " + e.getMessage());

            if (e instanceof InvocationTargetException) {
                InvocationTargetException invocationTargetException = (InvocationTargetException) e;

                Throwable currentException = invocationTargetException;
                if (invocationTargetException.getTargetException() != null) {
                    currentException = invocationTargetException.getTargetException();
                }

                result = StringUtils.buildExceptionMsg(currentException.getMessage());
            } else {
                result = StringUtils.buildExceptionMsg(e.getMessage());
            }
        }
        LogUtils.info(requestURI + "响应参数: " + result);

        //根据返回值进行后续处理
        String tager = "";
        if (result != null) {
            if (result instanceof String) {
                //转换返回值为字符串
                tager = result.toString();
                //检查是否为ajax的返回
                if (tager.startsWith("ajax:")) {
                    response.getWriter().write(tager.substring("ajax:".length()));
                } else if (tager.startsWith("redirect:")) {    //检查是否为重定向的
                    response.sendRedirect(tager.substring("redirect:".length()));
                } else if (tager.startsWith("forward:")) {   //检查是否为自定义的跳转（跳指定的其它controller）
                    request.getRequestDispatcher(tager.substring("forward:".length())).forward(request, response);
                } else if (tager.startsWith("msg:")) { //检查是否为消息提示
                    response.setContentType("text/html;charset=utf-8");
                    response.getWriter().write(tager.substring("msg:".length()));
                } else if (tager.endsWith(suffix)) {   //检查是否为自定义的跳转
                    request.getRequestDispatcher(tager).forward(request, response);
                } else if (tager.startsWith("exception:")) {   //检查是否为异常
                    response.setStatus(500);
                    response.setContentType("text/html;charset=utf-8");
                    response.getWriter().write(tager.substring("exception:".length()));
                }else {
                    tager = prefix + result + suffix;
                    //根据方法返回值做视图解析。
                    request.getRequestDispatcher(tager).forward(request, response);
                }
            } else {
                ObjectMapper objectMapper = new ObjectMapper();
                // 创建自定义日期格式（时分秒）
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                objectMapper.setDateFormat(sdf);
                String jsonStr = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(result);
//                System.out.println("jsonStr = " + jsonStr);

                response.setContentType("text/html;charset=utf-8");
                response.getWriter().write(jsonStr);
            }
        }
    }


    @Data
    class URLInfo {
        private String url;
        private Object obj;
        private Method method;
        private Map<String, String> paramInfoMap;

//        private Map<String, String> paramInfoMap;

        public URLInfo(String url, Object obj, Method method) {
            this.url = url;
            this.obj = obj;
            this.method = method;
            this.paramInfoMap = new LinkedHashMap<>();
            Class[] clazzes = method.getParameterTypes();
            String[] pNames = parameterNameDiscoverer.getParameterNames(method);
            for (int i = 0; i < pNames.length; i++) {
                paramInfoMap.put(pNames[i], clazzes[i].getSimpleName());
            }
        }


        public Object invoke(HttpServletRequest request, HttpServletResponse response) throws Exception {

            //获取方法的形参列表
            Set<String> keySet = paramInfoMap.keySet();
            //创建方法的实参列表
            List argList = new ArrayList();

            StringBuilder sb = new StringBuilder();
            sb.append(request.getRequestURI()).append("请求参数:   ");
            //如果是json方式提交参数
            if ("post".equals(request.getMethod().toLowerCase())
                    && request.getHeader("accept").startsWith("application/json")) {
                RequestJson requestJson = RequestJson.create(request);
                //参数类型转换并添加到实参列表
                for (String paramName : keySet) {
                    //获取前端提交的参数（字符串类型）
                    String paramValue = requestJson.getParameter(paramName);
                    //参数的类型转换及组建列表
                    switch (paramInfoMap.get(paramName)) {
                        case "ServletRequest":
                        case "HttpServletRequest":
                            argList.add(request);
                            break;
                        case "ServletResponse":
                        case "HttpServletResponse":
                            argList.add(response);
                            break;
                        case "HttpSession":
                            argList.add(request.getSession());
                            break;
                        case "PrintWriter":
                            response.setContentType("text/html;charset=utf-8");
                            argList.add(response.getWriter());
                            break;
                        case "short":
                        case "Short":
                            if (StringUtils.isNotEmpty(paramValue) && !"null".equals(paramValue)) {
                                argList.add(Short.parseShort(paramValue));
                            }
                            sb.append(paramName).append("=").append(paramValue).append(" & ");
                            break;
                        case "int":
                        case "Integer":
                            if (StringUtils.isNotEmpty(paramValue) && !"null".equals(paramValue)) {
                                argList.add(Integer.parseInt(paramValue));
                            }
                            sb.append(paramName).append("=").append(paramValue).append(" & ");
                            break;
                        case "long":
                        case "Long":
                            if (StringUtils.isNotEmpty(paramValue) && !"null".equals(paramValue)) {
                                argList.add(Long.parseLong(paramValue));
                            }
                            sb.append(paramName).append("=").append(paramValue).append(" & ");
                            break;
                        case "float":
                        case "Float":
                            if (StringUtils.isNotEmpty(paramValue) && !"null".equals(paramValue)) {
                                argList.add(Float.parseFloat(paramValue));
                            }
                            sb.append(paramName).append("=").append(paramValue).append(" & ");
                            break;
                        case "double":
                        case "Double":
                            if (StringUtils.isNotEmpty(paramValue) && !"null".equals(paramValue)) {
                                argList.add(Double.parseDouble(paramValue));
                            }
                            sb.append(paramName).append("=").append(paramValue).append(" & ");
                            break;
                        case "Date":
                            if (StringUtils.isNotEmpty(paramValue) && !"null".equals(paramValue)) {
                                argList.add(DateUtils.stringDate(paramValue));
                            }
                            sb.append(paramName).append("=").append(paramValue).append(" & ");
                            break;
                        default:    //如果是字符串
                            argList.add(paramValue);
                            sb.append(paramName).append("=").append(paramValue).append(" & ");
                    }
                }
                LogUtils.info(sb.substring(0, sb.length() - 3));
            } else {
                //参数类型转换并添加到实参列表
                for (String paramName : keySet) {
                    //获取前端提交的参数（字符串类型）
                    String paramValue = request.getParameter(paramName);

                    //参数的类型转换及组建列表
                    switch (paramInfoMap.get(paramName)) {
                        case "ServletRequest":
                        case "HttpServletRequest":
                            argList.add(request);
                            break;
                        case "ServletResponse":
                        case "HttpServletResponse":
                            argList.add(response);
                            break;
                        case "HttpSession":
                            argList.add(request.getSession());
                            break;
                        case "PrintWriter":
                            response.setContentType("text/html;charset=utf-8");
                            argList.add(response.getWriter());
                            break;
                        case "short":
                        case "Short":
                            if (StringUtils.isNotEmpty(paramValue) && !"null".equals(paramValue)) {
                                argList.add(Short.parseShort(paramValue));
                            }
                            sb.append(paramName).append("=").append(paramValue).append(" & ");
                            break;
                        case "int":
                        case "Integer":
                            if (StringUtils.isNotEmpty(paramValue) && !"null".equals(paramValue)) {
                                argList.add(Integer.parseInt(paramValue));
                            }
                            sb.append(paramName).append("=").append(paramValue).append(" & ");
                            break;
                        case "long":
                        case "Long":
                            if (StringUtils.isNotEmpty(paramValue) && !"null".equals(paramValue)) {
                                argList.add(Long.parseLong(paramValue));
                            }
                            sb.append(paramName).append("=").append(paramValue).append(" & ");
                            break;
                        case "float":
                        case "Float":
                            if (StringUtils.isNotEmpty(paramValue) && !"null".equals(paramValue)) {
                                argList.add(Float.parseFloat(paramValue));
                            }
                            sb.append(paramName).append("=").append(paramValue).append(" & ");
                            break;
                        case "double":
                        case "Double":
                            if (StringUtils.isNotEmpty(paramValue) && !"null".equals(paramValue)) {
                                argList.add(Double.parseDouble(paramValue));
                            }
                            sb.append(paramName).append("=").append(paramValue).append(" & ");
                            break;
                        case "Date":
                            if (StringUtils.isNotEmpty(paramValue) && !"null".equals(paramValue)) {
                                argList.add(DateUtils.stringDate(paramValue));
                            }
                            sb.append(paramName).append("=").append(paramValue).append(" & ");
                            break;
                        default:    //如果是字符串
                            argList.add(paramValue);
                            sb.append(paramName).append("=").append(paramValue).append(" & ");
                    }
                }
                LogUtils.info(sb.substring(0, sb.length() - 3));
            }

            return method.invoke(obj, argList.toArray());
        }
    }
}
